package i386CodeGenerator;

import ilanguage.IL_SSA_Depot;
import ilanguage.iltokens.IL_BaseSSA;
import symbolStorageService.ISymTable;
import view.PrintStream;


/**
 * The Class CodeGenerator.
 */

public class CodeGenerator {


    /** The Token streamer. */
    private IL_SSA_Depot SSAStreamer;

    /** The Instruction depot. */
    private PrintStream InstructionDepot;

    /** The Symbol table. */
    private ISymTable SymbolTable;

    /**
     * Instantiates a new code generator.
     *
     * @param TokenStreamer the token streamer
     * @param InstructionDepot the instruction depot
     * @param SymbolTable the symbol table
     */
    public CodeGenerator(IL_SSA_Depot TokenStreamer,
            PrintStream InstructionDepot, ISymTable SymbolTable) {
        this.SSAStreamer = TokenStreamer;
        this.InstructionDepot = InstructionDepot;
        this.SymbolTable = SymbolTable;
    }

    /**
     * Adds the header.
     */
    private void addHeader() {
        InstructionDepot.println(".MODEL small");
        InstructionDepot.println(".STACK 200h");
    }

    private void addCode(){
    	 InstructionDepot.println(".CODE\n");
    	 InstructionDepot.println(".386\n");
    	 InstructionDepot.println("\t;PROCEDIMIENTO OVERFLOW DOUBLE");
         InstructionDepot.println("\tdoubleoverflow proc ");
         InstructionDepot.println("\t\tjo error_doubleoverflow ");
         InstructionDepot.println("\t\tret ");
         InstructionDepot.println("\tdoubleoverflow endp ");
    	 InstructionDepot.println("\n");

    	 InstructionDepot.println("\t;PROCEDIMIENTO UNDERFLOW DOUBLE");
         InstructionDepot.println("\tdoubleunderflow proc ");
         InstructionDepot.println("\t\tjo error_doubleunderflow ");
         InstructionDepot.println("\t\tret ");
         InstructionDepot.println("\tdoubleunderflow endp ");
    	 InstructionDepot.println("\n");

    	 InstructionDepot.println("\t;PROCEDIMIENTO OVERFLOW INT");
         InstructionDepot.println("\tintoverflow proc ");
         InstructionDepot.println("\t\tjo error_intoverflow ");
         InstructionDepot.println("\t\tret ");
         InstructionDepot.println("\tintoverflow endp ");
    	 InstructionDepot.println("\n");

    	 InstructionDepot.println("\t;PROCEDIMIENTO UNDERFLOW INT");
         InstructionDepot.println("\tintunderflow proc ");
         InstructionDepot.println("\t\tjo error_intunderflow ");
         InstructionDepot.println("\t\tret ");
         InstructionDepot.println("\tintunderflow endp ");
    	 InstructionDepot.println("\n");

    	 InstructionDepot.println("\t;PROCEDIMIENTO DIVISION POR CERO");
         InstructionDepot.println("\tzerodiv proc ");
         InstructionDepot.println("\t\tjz error_zerodiv ");
         InstructionDepot.println("\t\tret");
         InstructionDepot.println("\tzerodiv endp");
    	 InstructionDepot.println("\n");

    	 InstructionDepot.println("\t;PROCEDIMIENTO PRINT");
         InstructionDepot.println("\tprint proc");
         InstructionDepot.println("\t\tmov ah,09h");
         InstructionDepot.println("\t\tint 21h");
         InstructionDepot.println("\t\tret");
         InstructionDepot.println("\tprint endp ");
    	 InstructionDepot.println("\n");


         InstructionDepot.println("\twarning_asig proc ");
         InstructionDepot.println("\t\tmov dx,offset _warninig_double");
         InstructionDepot.println("\t\tcall print ");
         InstructionDepot.println("\t\tret");
         InstructionDepot.println("\twarning_asig endp");
    	 InstructionDepot.println("\n");


         InstructionDepot.println("\terror_doubleoverflow:");
         InstructionDepot.println("\t\tmov dx,offset _error_overflow_double ");
         InstructionDepot.println("\t\tcall print");
         InstructionDepot.println("\t\tmov al,1h ");
         InstructionDepot.println("\t\tjmp SYSCALL_END\n");

         InstructionDepot.println("\terror_doubleunderflow:");
         InstructionDepot.println("\t\tmov dx,offset _error_underflow_double ");
         InstructionDepot.println("\t\tcall print");
         InstructionDepot.println("\t\tmov al,2h ");
         InstructionDepot.println("\t\tjmp SYSCALL_END\n");

         InstructionDepot.println("\terror_intoverflow:");
         InstructionDepot.println("\t\tmov dx,offset _error_overflow_int ");
         InstructionDepot.println("\t\tcall print");
         InstructionDepot.println("\t\tmov al,3h ");
         InstructionDepot.println("\t\tjmp SYSCALL_END\n");

         InstructionDepot.println("\terror_intunderflow:");
         InstructionDepot.println("\t\tmov dx,offset _error_underflow_int ");
         InstructionDepot.println("\t\tcall print");
         InstructionDepot.println("\t\tmov al,4h ");
         InstructionDepot.println("\t\tjmp SYSCALL_END\n");

         InstructionDepot.println("\terror_zerodiv:");
         InstructionDepot.println("\t\tmov dx,offset _error_div_by_zero ");
         InstructionDepot.println("\t\tcall print");
         InstructionDepot.println("\t\tmov al,5h ");
         InstructionDepot.println("\t\tjmp SYSCALL_END\n");

         InstructionDepot.println("\tSTART:");
         InstructionDepot.println("\t\tmov ax,@data ");
         InstructionDepot.println("\t\tmov ds,AX ");
         InstructionDepot.println("\t\tfinit");
         InstructionDepot.println("\t\tfclex");
    }
    /**
     * Declarar vars auxiliares.
     */
    private void DeclararVarsAuxiliares() {
    }

    /**
     * Generate machine code.
     * @throws NullPointerException si el codigo no se puede generar
     */
    public void GenerateMachineCode() {
        if (!this.SSAStreamer.hasErrors()) {
            this.addHeader();
            this.DeclararVars();
            this.addCode();
            this.addTercetosCodeMachine();
            this.DeclararVarsAuxiliares();
        }
    }

	private void addTercetosCodeMachine() {
    	InstructionDepot.println("\n\t;--------------Comienzo de tercetos--------------\n");
        for (IL_BaseSSA thing : SSAStreamer) {
        	String S= thing.getSequence();
            if (S!= "") InstructionDepot.print(thing.getSequence()+": ");
            InstructionDepot.print("\n\t;" + thing + "\n");
            if (!thing.ispostdiscartable())	InstructionDepot.print(thing.GenerateCode());

        }
        InstructionDepot.println("\n\t;--------------Fin de tercetos--------------");

	}

	private void DeclararVars() {
        InstructionDepot.println(".DATA");
        InstructionDepot.println("\t_error_overflow_double   DB   \'Runtime Error : double Overflow$\'");
        InstructionDepot.println("\t_error_underflow_double  DB   \'Runtime Error : double Underflow$\'");
        InstructionDepot.println("\t_error_overflow_int      DB   \'Runtime Error : integer Overflow$\'");
        InstructionDepot.println("\t_error_underflow_int     DB   \'Runtime Error : integer Underflow$\'");
        InstructionDepot.println("\t_error_div_by_zero       DB   \'Runtime Error : Division by ZERO!!!$\'");
        InstructionDepot.println("\t_warninig_double         DB   \'Runtime warning : double Value Trimmed$\'");
        InstructionDepot.println("\tFlagVar                  DW   ?");
        InstructionDepot.println("\tvarauxDouble             DQ   ?");
        InstructionDepot.println("\tvarauxInt                DW   ?");
        InstructionDepot.println(SymbolTable); // aca mete la tabla de simbolos en el asssembler
	}

}
